package huanzhao.erp.service;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import huanzhao.erp.controller.MiscController;
import huanzhao.erp.domain.entity.Notification;
import huanzhao.erp.domain.entity.NotificationCore;
import huanzhao.erp.domain.po.Result;
import huanzhao.erp.domain.types.NotificationType;
import huanzhao.erp.domain.vo.*;
import huanzhao.erp.mapper.*;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.validation.constraints.Size;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * @author April
 * 2024/6/13 下午9:03
 * @version 1.0
 */
@Service
@Slf4j
public class NotificationService extends ServiceImpl<NotificationMapper, Notification> {
    private static final ThreadPoolExecutor executor;

    static {
        // 获取cpu数量
        int cpuNum = Runtime.getRuntime().availableProcessors();
        // 使用较少的线程数，避免影响到核心业务
        executor = new ThreadPoolExecutor(cpuNum >> 1, cpuNum + 1, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy());
        executor.allowCoreThreadTimeOut(true);
    }

    private final BasicService basicService;
    private final NotificationMapper notificationMapper;
    private final StaffMapper staffMapper;
    private final ConvertService convertService;


    public NotificationService(BasicService basicService, NotificationMapper notificationMapper, PhoneMapper phoneMapper, TaskMapper taskMapper, ExceptionMapper exceptionMapper, FinanceSettlementMapper financeSettlementMapper, StaffMapper staffMapper, ConvertService convertService) {

        this.basicService = basicService;
        this.notificationMapper = notificationMapper;
        this.staffMapper = staffMapper;
        this.convertService = convertService;
    }

    /**
     * 已读所有通知
     *
     * @param recipientId 接收者id
     * @return Result<?>
     * 2024/6/13 下午9:06:56
     */
    public Result<?> readAllNotifications(Long recipientId) {
        try {
            notificationMapper.readAllNotifications(recipientId);
            return Result.ok("操作成功");
        } catch (Exception e) {
            log.error("已读所有通知失败", e);
            return Result.error("其他错误，请联系开发人员处理");
        }
    }

    /**
     * 已读通知
     *
     * @param id          通知id
     * @param recipientId 接收者id
     * @return Result<?>
     * 2024/6/13 下午9:11:59
     */
    public Result<?> readNotification(Long id, Long recipientId) {
        try {
            notificationMapper.readNotification(id, recipientId);
            return Result.ok("操作成功");
        } catch (Exception e) {
            log.error("已读通知失败", e);
            return Result.error("其他错误，请联系开发人员处理");
        }
    }

    /**
     * 按条件查询通知消息
     *
     * @param recipientId 接收者id
     * @param type        消息类型
     * @param pageNum     当前页码
     * @param pageSize    每页记录数量
     * @return IPage<Notification>
     * 2024/6/13 下午9:16:26
     */
    public IPage<NotificationVO> getNotificationsByCondition(Long recipientId, String type, Integer pageNum, Integer pageSize) {
        try {
            IPage<Notification> page = notificationMapper.getNotificationsByCondition(recipientId, type, Page.of(pageNum, pageSize));
            List<Notification> notifications = page.getRecords();
            if (notifications == null) {
                return new Page<>();
            }
            List<NotificationVO> result = new ArrayList<>();
            notifications.forEach(notification -> result.add(convertService.convertNotification(notification)));
            return new Page<NotificationVO>().setRecords(result).setTotal(page.getTotal());
        } catch (Exception e) {
            log.error("按条件查询通知消息失败", e);
            return new Page<>();
        }
    }

    /**
     * 获取未推送的通知
     *
     * @param recipientId 接收者id
     * @param limit       限制数量
     * @return List<Notification>
     * 2024/6/21 下午1:48:19
     */
    public List<NotificationVO> getUnpushedNotificationsInLimit(long recipientId, long limit) {
        List<Notification> notifications = notificationMapper.getUnpushedNotificationsInLimit(recipientId, limit);
        if (notifications == null) {
            return Collections.emptyList();
        }
        // 根据通知类型设置相关对象
        List<NotificationVO> result = new ArrayList<>();
        notifications.forEach(notification -> result.add(convertService.convertNotification(notification)));
        return result;
    }

    /**
     * 获取未推送的通知数量
     *
     * @param recipientId 接收者id
     * @return long
     * 2024/6/21 下午9:51:13
     */
    public long getUnpushedNotificationsCount(long recipientId) {
        return notificationMapper.getUnpushedNotificationsCount(recipientId);
    }

    /**
     * 将目标消息标记为已推送
     *
     * @param recipientId 接收者id
     *                    2024/6/21 下午10:09:31
     */
    public void pushedNotifications(long recipientId) {
        notificationMapper.pushedNotifications(recipientId);
    }

    /**
     * 移除SseEmitter
     *
     * @return SseEmitter
     * 2024/6/21 下午10:18:28
     */
    public SseEmitter removeEmitter(long recipientId) {
        try {
            return MiscController.emitters.remove(recipientId);
        } catch (Exception e) {
            log.error("移除SseEmitter失败", e);
            return null;
        }
    }

    /**
     * 向业务经理和仓管部下的负责人、员工发送通知
     *
     * @param notificationType 通知类型
     * @param content          通知内容
     * @param relatedId        关联id
     *                         2024/6/22 上午11:11:38
     */
    public void sendNotificationToWarehouse(NotificationType notificationType, String content, Long relatedId) {
        Long handlerId = basicService.getGlobalSetting().getWarehouseHandlerId();
        Long managerId = basicService.getGlobalSetting().getManagerId();
        Set<Long> receiverIds = new HashSet<>(Set.of(handlerId,managerId));
        List<Long> staffIds = staffMapper.getAllWarehouseStaffId();
        if (staffIds != null) {
            receiverIds.addAll(staffIds);
        }
        batchSendNotification(receiverIds,NotificationType.PHONE_TIMEOUT, content, relatedId);
    }

    /**
     * 批量发送通知
     *
     * @param notificationType 通知类型
     * @param content          通知内容
     * @param relatedId        关联id
     * @param staffIds         员工id列表
     *                         2024/10/10 10:09:35
     */
    public void batchSendNotification(@NonNull @Size(min = 1) Collection<Long> staffIds,
                                      NotificationType notificationType,
                                      String content,
                                      Long relatedId) {
        NotificationCore coreInfo = new NotificationCore(notificationType, content, relatedId);
        notificationMapper.addNotificationCoreInfo(coreInfo);
        for (Long staffId : staffIds) {
            sendNotification(staffId, coreInfo);
        }
    }

    public void sendNotification(Long recipientId, NotificationType notificationType, String content, Long relatedId) {
        NotificationCore coreInfo = new NotificationCore(notificationType, content, relatedId);
        notificationMapper.addNotificationCoreInfo(coreInfo);
        sendNotification(recipientId, coreInfo);
    }

    /**
     * 向指定用户发送通知
     *
     * @param recipientId 接收者id
     * @param coreInfo    通知核心信息
     *                    2024/6/22 上午11:21:48
     */
    public void sendNotification(Long recipientId, NotificationCore coreInfo) {
        executor.execute(() -> {
            // 若用户在线则推送通知
            boolean pushed = false;

            SseEmitter emitter = MiscController.emitters.get(recipientId);
            NotificationVO notificationVO = BeanUtil.copyProperties(coreInfo, NotificationVO.class);
            if (emitter != null) {
                try {
                    // 根据通知类型设置相关对象
                    convertService.fillNotificationVO(notificationVO, coreInfo.getRelateId());
                    emitter.send(SseEmitter.event().name("message").data(basicService.parseObj2Json(notificationVO)));
                    pushed = true;
                } catch (Exception e) {
                    log.error("向用户推送通知失败，接收者id：{}，通知类型：{}，通知内容：{}", recipientId, notificationVO.getType(), notificationVO.getContent(), e);
                }
            }

            // 添加通知
            notificationMapper.addNotification(recipientId, coreInfo.getId(), pushed);
        });
    }


    /**
     * 获取未读通知数量
     *
     * @param recipientId 消息接收方id
     * @return Long
     * 2024/7/4 下午2:46:12
     */
    public Long getUnreadNotificationsCount(Long recipientId) {
        return notificationMapper.getUnreadNotificationsCount(recipientId);
    }
}
